// 画布、一级节点、二级节点（设备）、组件
import Konva from 'konva'
import { contextmenuEvt } from './evt.js'
import { createLifeLine, createChildNode } from './components.js'
import './style.scss'

const TWOWIDTH = 14
const CELLWIDTH = 200

/** @type { import('./type.d.ts').INode[] } */
const dones = [
  {
    id: '1',
    label: '开始',
    color: '#07cdb1',
    width: CELLWIDTH,
    buttonProps: {
      w: 100,
      h: 32,
      r: 4,
      top: 40
    },
    children: [
      {
        id: '1-1',
        label: '开始时间: 2024-3-15 15:54:13',
        width: TWOWIDTH,
        height: 192,
        color: '#07cdb1',
        data: {}
      }
    ]
  }

]
// lifelineComponents
export class FlowChart {
  /** 右键鼠标事件触发时页面dom弹出卡片内容
   * @type { HTMLElement | null }
   */
  contextmenuDom = null
  /** 右键菜单内容 */
  menu = [
    {
      name: '添加一级节点',
      add: (e) => {
        this.addBrotherNode(e)
      }
    },
    {
      name: '添加子节点',
      add: (e) => {
        this.addChildrenNode(e)
      }
    },
    {
      name: '删除当前节点',
      add: (e) => {
        this.deleteNode(e)
      }
    }
  ]
  /** 节点数组 @type { import('./type.d.ts').INode[] } */
  nodes = []
  /** lifeLine组件数据 */
  lifeLineComponents = []

  bottom = 0

  /** 当前选中节点的内容 */
  active = null
  /** 当节点内容发生改变时触发的回调函数,参数为新的节点内容 */
  onChange = (active) => {}
  /** @type { HTMLDivElement } */
  currentDom = null
  currentDomPostion = [0, 0]
  /** @type { (e: HTMLDivElement) => void } */
  onCurrentDomChange = (e) => {}

  /**
   * @param { string } domId
   * @param { boolean } readony 是否只读
   */
  constructor(domId, readony) {
    this.readony = readony
    const dom = document.getElementById(domId)
    if (!dom) {
      console.error('domId is not exist')
      return
    }

    dom.style.position = 'relative'

    this.stage = new Konva.Stage({
      name: 'stage',
      container: domId,
      width: dom.clientWidth,
      height: dom.clientHeight,
      draggable: true
    })
    this.layer = new Konva.Layer({ id: 'layer' })
    this.stage.add(this.layer)
    this.bottom = this.stage.height()

    // 鼠标右键事件监听
    readony || contextmenuEvt(this.stage, this)
    // this.setNodes(dones)
    if (this.bottom > this.stage.height()) {
      this.setNodes(this.nodes)
    }
    this.observe = new ResizeObserver((entries) => {
      entries.forEach((entry) => {
        const { width, height } = entry.contentRect
        this.stage.size({ width, height })
      })
    })
    this.observe.observe(dom)
    this.init()
  }
  init() {
    console.log('init')
    this.stage.on('dragmove', (e) => {
      const { x, y } = e.target.attrs
      if (this.currentDom) {
        this.currentDom.style.left = this.currentDomPostion[0] + x + 'px'
        this.currentDom.style.top = this.currentDomPostion[1] + y + 'px'
      }
    })
  }
  // 关闭右键菜单
  closeContextMenu() {
    this.contextmenuDom && this.contextmenuDom.remove()
    this.contextmenuDom = null
  }
  setNodes(nodes) {
    this.nodes = nodes
    this.layer.removeChildren()
    // this.layer.draw()

    this.lifeLineComponents = []

    this.nodes.forEach((node, index) => {
      const cWidth = (node.width || CELLWIDTH)
      const lifeLine = createLifeLine({
        id: node.id,
        x: index * cWidth,
        width: cWidth,
        color: node.color,
        label: node.label,
        buttonProps: node.buttonProps,
        height: this.bottom + 50
      }, this.stage, this)
      this.layer.add(lifeLine)
      this.lifeLineComponents.push(lifeLine)

      let oIndex = 0
      lifeLine.on('mousedown', (e) => {
        if (e.target.name() === 'lifeLine' && !this.readony) {
          this.setActiveNode({
            isParent: true,
            ...node
          })
        }
        oIndex = Math.abs(Math.round(lifeLine.x() / 200))
        lifeLine.moveToTop()
        const fn = (e) => {
          let toIndex = Math.abs(Math.round(lifeLine.x() / cWidth))
          if (toIndex > this.nodes.length - 1) {
            toIndex = this.nodes.length - 1
          }
          this.nodes = swapThePositionOfAnArray(this.nodes, oIndex, toIndex)
          this.lifeLineComponents = swapThePositionOfAnArray(this.lifeLineComponents, oIndex, toIndex)

          this.lifeLineComponents.forEach((n, i) => {
            new Konva.Tween({
              node: n,
              x: i * cWidth,
              y: 0,
              duration: 0.3
              // onFinish: () => {
              //   this.setNodes(this.nodes)
              // }
            }).play()
          })
          document.removeEventListener('mouseup', fn)
        }
        e.evt.button === 0 && document.addEventListener('mouseup', fn)
      })

      let y = 40 + 30 + 50
      node?.children?.forEach((children, idx) => {
        // if (index > 0 && idx === 0) {
        //   y -= children.height + 20
        // }
        const rect = createChildNode({
          id: children.id,
          x: cWidth / 2 - children.width / 2,
          y,
          cellWidth: cWidth,
          ...children
        }, this)
        lifeLine.add(rect)

        y += children.height + 20

        rect.on('click', (e) => {
          if (e.target.name() === 'childNode') {
            this.setActiveNode({
              isChildren: true,
              ...children
            })
          }
        })
      })
    })
  }
  update(v) {
    const fn = (nodes) => {
      nodes.some(n => {
        if (n.id === v.id) {
          Object.keys(n).forEach(k => {
            n[k] = v[k]
          })
          return true
        }
        if (n.children) {
          return fn(n.children)
        }
      })
    }
    fn(this.nodes)

    this.setNodes(this.nodes)
    const d = this.stage.find(`#${v.id}`)
    d[0]?.fire('click')
    this.active = v
  }
  // 添加兄弟节点
  addBrotherNode(e) {
    const name = e.target.attrs.name
    if (name === 'stage') {
      this.nodes.push({
        id: uid(),
        label: '新节点',
        color: '#00c2ff',
        width: CELLWIDTH,
        buttonProps: {
          w: 100,
          h: 32,
          r: 4,
          top: 40
        }
      })
      this.setNodes(this.nodes)
    } else if (name === 'lifeLine') {
      const i = e.target.parent.attrs.x / e.target.attrs.width

      this.nodes.splice(i + 1, 0, {
        id: uid(),
        label: '新节点',
        color: '#00c2ff',
        width: CELLWIDTH,
        buttonProps: {
          w: 100,
          h: 32,
          r: 4,
          top: 40
        }
      })
      this.setNodes(this.nodes)
    }
    this.closeContextMenu()
  }
  // 删除当前节点
  deleteNode(e) {
    const name = e.target.attrs.name
    if (name === 'lifeLine') {
      const i = e.target.parent.attrs.x / e.target.parent.attrs.width

      this.nodes.splice(i, 1)
      this.setNodes(this.nodes)
    } else if (name === 'childNode') {
      const id = e.target.attrs.id
      this.nodes.some((n, i) => {
        return n.children?.some((c, idx) => {
          if (c.id === id) {
            this.nodes[i].children.splice(idx, 1)
            return true
          }
        })
      })
      this.setNodes(this.nodes)
      this.stage.draw()
    }

    this.closeContextMenu()
  }
  // 添加子节点
  addChildrenNode(e) {
    const name = e.target.attrs.name
    if (name === 'lifeLine') {
      const i = e.target.parent.attrs.x / e.target.parent.attrs.width

      this.nodes[i].children ||= []
      this.nodes[i].children.push({
        id: uid(),
        label: '新节点',
        color: '#00c2ff',
        labelPosition: 'right',
        width: TWOWIDTH,
        height: 100,
        data: {}
      })
      this.setNodes(this.nodes)
      this.closeContextMenu()
      if (this.bottom > this.stage.height()) {
        this.setNodes(this.nodes)
      }
    }
  }

  setActiveNode(node) {
    this.active = node
    this.onChange(node)
    if (!node) return
    const d = this.stage.find(`#${node.id}`)
    if (Array.isArray(d) && d.length > 0) {
      d[0].fire('click')
      this.readony && this.addCurrentDom(d[0])
    }
    console.log('this.active', this.active, d)
  }

  // 添加当前点击节点dom元素
  addCurrentDom(d) {
    let x1 = 0
    let y2 = 0
    const fn = (i) => {
      if (i.attrs.name == 'stage') return
      const { x, y } = i.attrs
      if (x) (x1 += x)
      if (y) (y2 += y)
      if (i.parent) fn(i.parent)
    }
    fn(d)
    const { x, y } = d.getAbsolutePosition()
    this.currentDomPostion = [x1, y2]
    if (this.currentDom) {
      this.stage.container().removeChild(this.currentDom)
      this.currentDom = null
    }
    const dom = document.createElement('div')
    dom.style.position = 'absolute'
    dom.style.left = `${x}px`
    dom.style.top = `${y}px`
    dom.innerText = ''
    this.stage.container().appendChild(dom)
    this.currentDom = dom
    this.onCurrentDomChange(this.currentDom)
  }
  // 删除当前元素
  deleteCurrentDom() {
    if (this.currentDom) {
      this.stage.container().removeChild(this.currentDom)
      this.currentDom = null
      this.currentDomPostion = [0, 0]
    }
  }

  setCurrentColor(node, color) {
    const id = node?.id
    if (id) {
      const d = this.stage.find(`#${id}`)
      d && (d[0].attrs.fill = color)
      this.stage.draw()
    }
  }

  // 元素销毁时的方法
  destroy() {
    this.observe.disconnect()
  }
}

/**
 * @param {any[]} arr
 * @param {number} o1 - 需要移动的元素的下标
 * @param {number} to1 - 移动到的下标
 */
const swapThePositionOfAnArray = (arr, o1, to1) => {
  const a1 = arr.slice(0, o1)
  const a2 = arr.slice(o1 + 1, arr.length)
  if (to1 < o1) {
    a1.splice(to1, 0, arr[o1])
  } else if (to1 > o1) {
    a2.splice(to1 - o1, 0, arr[o1])
  } else {
    return arr
  }
  return a1.concat(a2)
}

const uid = () => {
  return Math.random().toString(36).substr(2, 15)
}
